package com.xjy.rpc.netty.client;

import com.xjy.rpc.cache.ChannelCache;
import com.xjy.rpc.netty.handler.*;
import com.xjy.rpc.netty.handler.client.ClientMessageHandler;
import com.xjy.rpc.netty.handler.listener.ReConnectListener;
import com.xjy.rpc.netty.message.RpcMessage;
import com.xjy.rpc.utils.SocketParamUtils;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;


import java.net.InetAddress;
import java.net.UnknownHostException;


@Slf4j
public class Client {
    public static void main(String[] args) {
        Client clientTest = new Client("127.0.0.1",8083);
        //Client clientTest = new Client("10.14.0.32",8083);
        //clientTest.init();
        clientTest.connect();

        /*IProxy proxyBean = ProxyFactory.getProxy("CGLIB");
        ITestController proxy = proxyBean.getProxy(testController.class);
        proxy.hello();*/
        //TODO 以下操作优化为动态代理
        /*RpcMessage rpcMessage = new RpcMessage();
        rpcMessage.setRequestId(new Random().nextLong());
        rpcMessage.setMessageType((short) 0x10);
        rpcMessage.setExpireTime(100);
        rpcMessage.setUnit(TimeUnit.SECONDS);
        MessageBody body = new MessageBody();
        body.setIp("127.0.0.1");
        body.setPort(8083);
        body.setInterfaceName("com.xjy.rpc.controller.testController");
        body.setMethodName("hello");
        body.setMethodResponseType("String");
        rpcMessage.setBody(body);
        clientTest.sendMessage(rpcMessage);*/

    }

    private Channel channel;

    private final String serviceType = "cli";

    private String ip;

    {
        try {
            ip = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        }
    }

    @Value("${socket.port}")
    private Integer port;

    private NioEventLoopGroup worker;

    private Bootstrap bootstrap;

    public Client(String ip,Integer port){
        this.ip = ip;
        this.port = port;
        init();
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    public void init(){
        bootstrap = new Bootstrap();
        worker = new NioEventLoopGroup();

        bootstrap.group(worker).channel(NioSocketChannel.class)
                //设置检测连接
                .option(ChannelOption.SO_KEEPALIVE, true)
                //设置端口复用
                //.option(ChannelOption.SO_REUSEADDR, true)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel channel) throws Exception {
                        ChannelPipeline pipeline = channel.pipeline();
                        //服务断线处理
                        pipeline.addLast(new ReconnectHandler(Client.this));
                        //心跳
                        //pipeline.addLast(new ClientHeartBeatHandler());
                        //pipeline.addLast("ping",new IdleStateHandler(10,10,60*10, TimeUnit.SECONDS));
                        //编码器
                        pipeline.addLast(new RpcEncoder());
                        //解码器
                        pipeline.addLast(new RpcDecoder());
                        //客户端消息解析
                        pipeline.addLast(new ClientMessageHandler());
                    }
                });
    }

    public void connect(){
        try {
            ChannelFuture future = bootstrap.connect(ip, port);
            //添加实现断线重连，重试失败后释放资源
            future.addListener(new ReConnectListener(this));
            future.sync();
            channel = future.channel();
            ChannelCache.channelMap.put(
                    SocketParamUtils.getKeyByIp(ip,port)
                    ,channel);
            log.info("socket缓存成功 key:{}",SocketParamUtils.getKeyByIp(ip,port));
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void sendMessage(RpcMessage msg){
        channel.writeAndFlush(msg);
    }

    public void shutdown(){
        log.info("资源释放");
        if (worker != null) worker.shutdownGracefully();
    }
}

